import { ShElement } from './ShElement'


/**
 * 弹出面板在弹出动作时的选项。
 */
export interface ShPopupOptions {
    /**
     * 指定一个参考元素，弹出框会相对于该元素进行定位。
     * 
     * 如果是 `null`，则忽略 `position` 和 `anchor` 选项，以窗口左上角为参考点，`offset` 选项指定位置。
     */
    $ref: HTMLElement | null
    /**
     * 参考元素的四个顶点之一，作为参考点。
     */
    position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
    /**
     * 本弹出面板的四个顶点之一，作为锚定点。
     */
    anchor: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
    /**
     * 本弹出面板锚定点相对于参考点的偏移。如果没指定参考元素，则该偏移是相对于视口左上角的。
     */
    offset: {
        top: number
        left: number
    }
}


export class ShPopup extends ShElement {
    
    private _options: ShPopupOptions = {
        $ref: null,
        position: 'top-left',
        anchor: 'top-left',
        offset: { top: 0, left: 0 },
    }

    private _hideTimeout = -1

    constructor() {
        super()

        this.$shadow.innerHTML = `
            <style>
                :host {
                    position: fixed;
                    border: var(--border-ctrl);
                    background-color: white;
                    color: black;
                    box-shadow: 2px 2px 6px var(--color-shadow);
                    cursor: default;
                    z-index: 1000;

                    visibility: hidden;
                    opacity: 0;
                    transition: visibility .3s, opacity .3s;
                }
                :host(.show) {
                    visibility: visible;
                    opacity: 1;
                }
            </style>
            <slot></slot>
        `
    }

    /**
     * 判断本弹出面板是否处于可见状态。
     */
    public isVisible(): boolean {
        return window.getComputedStyle(this).opacity === '1'
    }

    public show(options?: ShPopupOptions) {
        if (options)
            this._options = options

        if (this.isVisible())
            return

        // 如果是 hide() 的 300ms 动画效果过程中，则立即结束动画。
        if (this._hideTimeout > 0) {
            window.clearTimeout(this._hideTimeout)
            this.remove()
            this._hideTimeout = -1
        }

        // 计算显示位置
        let top = this._options.offset.top
        let left = this._options.offset.left
        if (this._options.$ref) {
            let r = this._options.$ref.getBoundingClientRect()
            top += r.top
            left += r.left
            if (this._options.position.startsWith('bottom'))
                top += r.height
            if (this._options.position.endsWith('right'))
                left += r.width

            r = this.getBoundingClientRect()
            if (this._options.anchor.startsWith('bottom'))
                top -= r.height
            if (this._options.anchor.endsWith('right'))
                left -= r.width
        }

        // 更新 CSS，显示本弹出面板
        this.updateStyle({
            top: top + 'px',
            left: left + 'px',
        })
        document.body.append(this)
        window.setTimeout(() => this.addClass('show'))

        // 只有在可见时才响应 clickout 事件
        this.enableClickout()

        this.dispatchEvent(new Event('shev-popup-show'))
    }

    public showAt(x: number, y: number) {
        const saveOptions = this._options
        this.show({
            $ref: null,
            position: 'top-left',
            anchor: 'top-left',
            offset: { top: x, left: y }
        })
        this._options = saveOptions
    }

    public hide() {
        // 更新透明度
        this.removeClass('show')

        // 300毫秒 CSS 动画效果结束，移除 DOM
        this._hideTimeout = window.setTimeout(() => {
            this.remove()
            this._hideTimeout = -1
        }, 300)

        // 取消关注 clickout 事件
        this.disableClickout()

        this.dispatchEvent(new Event('shev-popup-hide'))        
    }
}
customElements.define('sh-popup', ShPopup)